這一天小明問我甚麼是Repository ?
很多應用程式都有資料存取的需求,大多數使用的都是關聯式資料庫 (RDBMS),
大多數的開發人員使用ODBC, DB-Library, ADO.NET, OLE DB 等不同的介面,
再搭配 SQL 指令來存取資料.
為了要整合這麼多種資料儲存類型,最好的方法就是將要儲存的物件抽取出來變成一個實體物件(Entity),
有了Entity 物件後,就可以試著傳遞Entity 資料到某個介面interface,以達到儲存或讀取Entity 的能力,
這個抽象化介面就是 Repository .
只要向資料儲存體Repository 存放資料或要讀取資料,如果資料儲存體換了,
只要抽換掉 Repository interface 的實作即可,
上層的服務完全不需要異動, 而這也是 Repository 最終的目的。
Repository 有很多種實作方法,有很多人用一個Repository 代表一個DB 裡面的 Table.
為了減少重複的程式碼, 也有人是以Generic 的方式,寫一種通用型的Repository , 如下示範通用型的IRepository
public interface IRepository<T>
{
void Create(T entity);
T Read(Expression<Func<T, bool>> predicate);
void Update(T entity);
void Delete(T entity);
void SaveChanges();
}
如果我同時要用2 個Table 以上怎麼辦?有人就寫了IUnitOfWork 來做這件事情.
以下是 IUnitOfWork 程式碼,
public interface IUnitOfWork
{
IGenericRepository<Customer> Customer { get; }
IGenericRepository<Order> Order { get; }
Task<int> SaveChangeAsync();
}
熟悉Entity Framework 的人, 可以發現這跟Entity Framework 架構一樣,
上述IUnitOfWork 程式碼可以呼應對應到下面的Entity Framework 程式碼
public MyDbContext : DbContext
{
public DbSet<Customer> Customer { get; set; }
public DbSet<Order> Order { get; set; }
}
在ORM / Entity Framework , 還沒那麼成熟的年代,
使用 Repository 模式的理由很充分,好處也明顯.
當 Entity Framework 和 NHibernate 已經提供了同樣, 甚至更多的功能, 我們還需要再多加一層 Repository 嗎?
網路上可以找到很多文章,介紹如何搭配 Entity Framework 來設計我們的 Repository.
所以「怎麼做」並不是問題,問題在於我們是否該這麼做?
假如你原本用Entity Framework 去存取SQL Server,
有一天你想改為Sqlite, 你也只需提供DB Provider , 外層的程式碼也不需要改動.
也能存取各種不同的資料庫.
拿掉Repository 之後,單元測試還是可以做,只要在資料庫存取方法提升到interface method,
仍然可以進行隔離測試.
其次,可抽換底層的資料存取元件當然很理想. 然而就算我們用Repository 把它包起來,難保不夠周密.
除了擔心開發時間成本效益,我也懷疑真實世界中有多少案子真的需要而且實際有「任意切換不同的儲存媒體」的需求.
至於Unit of Work,其實 EF 的 ObjectContext 和 DbContext 已經提供了類似的功能.
如果在ORM 之上再疊一層 Repository,會讓我們費好大功夫寫一堆看起來長得很像的程式碼,
增加程式的複雜性,卻得不到相對比例的好處.
就我的觀點而言,在不需要切換任意 實際儲存資料體 的情況下,比較小而單純的應用程式,
我不會使用Repository . 如果是比較大型或複雜的系統....嗯,
目前我是沒遇過同一套系統還需要切換不同種類的資料儲存媒體.